package cachestring

import (
	"errors"
	"fmt"
	"os"
	"strconv"
	"sync"
	"sync/atomic"
	"time"
	"willow/common/conf"
	"willow/common/logs"
	"willow/common/sdk"
)

/**
 * 	自适应字符串结构体
 */
type SdsString struct {
	value      []byte	 `json:"value"`
	mollocLen  int       `json:"molloc_len"`			//分配的内存空间
	useLen     int       `json:"use_len"` 				//已使用的内存空间
	surplusLen int       `json:"surplus_len"` 			//剩余的内存空间
	setTime    int64	 `json:"set_time"`				//创建字符串的时间
	updateTime int64	 `json:"update_time"`			//修改字符串时间
	outTime    int64     `json:"out_time"`				//过期时间
}

type SdsValueExist struct{
	value 	   []byte 									//value值
	exist      int 										//判断value是否存在
}

var sdsMap map[string]SdsString

const INT_MAX = int(^uint(0) >> 1)

var timeOutCAS  int

var timeOut  int64

func init()  {

}

/**
 * 初始化sdsMap
 */
func InitConst()  {
	once := sync.Once{}
	once.Do(func() {
		sdsMap = make(map[string]SdsString)
		if val,ok := conf.WillowConf["sdsCASTimeout"];ok{
			timeOutCAST,err := strconv.ParseInt(val,10,0)
			if err != nil {
				logs.ErrorLog("timeOutCAS : "+err.Error())
			}
			timeOutCAS = int(timeOutCAST)
		} else {
			timeOutCAS = INT_MAX
		}

		if val,ok := conf.WillowConf["sdsTimeOut"];ok{
			timeOutTmp,err := strconv.ParseInt(val,10,64)
			if err != nil {
				logs.ErrorLog("sdsTimeOut : "+err.Error())
			}
			timeOut = timeOutTmp*1e9
		}
	})
}

/**
 *	设置字符串
 */
func (sds *SdsString) SetValue(key string, value string) int8 {
	var sdsT SdsString
	if sdsTs, ok := sdsMap[key]; !ok {
		sdsT = SdsString{
			value:      []byte{},
			mollocLen:  0,
			useLen:     0,
			surplusLen: 0,
			setTime:    time.Now().UnixNano(),
			outTime:	timeOut,
		}
	} else {
		sdsT = sdsTs
	}

	var i int
	for i = 0 ;i < timeOutCAS ; i++ {
		sdsT.updateTime = time.Now().UnixNano()
		tempTime		:= sdsT.updateTime
		sds.setLen(value, &sdsT)
		if tempTime == sdsT.updateTime {
			sdsMap[key] = sdsT
			return 1
		}
	}
	return -1
}

/**
 *	只有在 key 不存在时设置 key 的值
 */
func (sds *SdsString) SetnxValue(key string, value string) int8 {
	var sdsT SdsString
	if _, ok := sdsMap[key]; !ok {
		sdsT = SdsString{
			value:      []byte{},
			mollocLen:  0,
			useLen:     0,
			surplusLen: 0,
			setTime:    0,
			outTime:	timeOut,
		}
	} else {
		return -1
	}
	var i int
	for i = 0 ;i < timeOutCAS ; i++ {
		sdsT.updateTime = time.Now().UnixNano()
		tempTime		:= sdsT.updateTime
		sds.setLen(value, &sdsT)
		if tempTime == sdsT.updateTime {
			sdsMap[key] = sdsT
			return 1
		}
	}
	return -1
}

/**
 *	为字符串分配一定的空间的存储
 */
func (sds *SdsString) setLen(value string, sdsT *SdsString) {
	temp := len(value)

	if temp < sdsT.mollocLen {
		for i := 0; i < temp; i++ {
			sdsT.value[i] = value[i]
		}
		sdsT.useLen = temp
		sdsT.surplusLen = sdsT.mollocLen - temp
		sdsT.setTime = time.Now().UnixNano()
	} else {
		var tempLen int
		if sdsT.mollocLen < 1024 {
			tempLen = temp * 2
		} else {
			tempLen = temp/4 + tempLen
		}

		sdsT.value = make([]byte, tempLen)
		for i := 0; i < len(value); i++ {
			sdsT.value[i] = value[i]
		}
		sdsT.mollocLen = tempLen
		sdsT.useLen = len(value)
		sdsT.surplusLen = tempLen - len(value)
		sdsT.setTime = time.Now().UnixNano()
	}
}

/**
 * 设置过期时间
 */
func (sds *SdsString) SetOutTime(key string, outTime int64) int64 {
	if sdsTs, ok := sdsMap[key]; ok {
		sdsTs.outTime = outTime*1e9
		sdsMap[key] = sdsTs
		return 1
	}
	return -1
}

/**
 * 返回剩余过期时间
 */
func (sds *SdsString) GetOutTime(key string) int64 {
	if sdsTs, ok := sdsMap[key]; ok {
		second := (sdsTs.setTime+sdsTs.outTime-time.Now().UnixNano())/1e9
		if second > 0  {
			return second
		}
		sds.DeleteSafeValue(key)
	}
	return -1
}

/**
 *	获取对应的key的value如果该key没有对应的value值，则返回控制
 *
 */
func (sds *SdsString) GetValue(key string) string {
	if sdsTs, ok := sdsMap[key]; ok {
		if (sdsTs.setTime+sdsTs.outTime-time.Now().UnixNano())/1e9 > 0 {
			return sdk.ByteString(sdsTs.value)
		}
		sds.DeleteSafeValue(key)
	}
	return ""
}

/**
 *	删除对应key的value值
 */
func (sds *SdsString) DeleteValue(key string) {
	delete(sdsMap, key)
}

/**
 * 获取字符串的长度（返回的是SDS已使用空间字节数）
 */
func (sds *SdsString) GetLen(key string) int {
	if val,ok := sdsMap[key];ok {
		return val.useLen
	}
	return -1
}

/**
 *	安全删除对应key的value值
 *	互斥锁锁定操作的逆操作并不会导致协程阻塞，但是有可能导致引发一个无法恢复的运行时的panic
 */
func (sds *SdsString) DeleteSafeValue(key string) {
	defer func() {
		if p := recover(); p != nil {
			fmt.Println("recover the panic : ", p)
		}
	}()
	var mutex sync.Mutex
	mutex.Lock()
	delete(sdsMap, key)
	mutex.Unlock()
}

/**
 * 获取所有(一个或多个)给定 key 的值。
 * 
 */
func (sds *SdsString) MGet(keys []string) []SdsValueExist {
	val  := []SdsValueExist{}
	var  temp SdsValueExist
	for i := 0; i < len(keys); i++ {
		if sdsTs, ok := sdsMap[keys[i]]; ok {
			temp.value = sdsTs.value[:]
			temp.exist = 1
		} else {
			temp.value = nil
			temp.exist = -1
		}
		val = append(val,temp)
	}
	return val
}

/**
 * 同时设置一个或多个 key-value 对
 */
func (sds *SdsString)MSet(keyValue[][2]string) int64 {
	for i := 0; i < len(keyValue) ; i++   {
		sds.SetValue(keyValue[i][0],keyValue[i][1])
	}
	return 0
}

/**
 * 同时设置一个或多个 key-value 对，当且仅当所有给定 key 都不存在
 */
func (sds *SdsString) MSetnx(keyValue[][2]string) int64 {
	for i := 0; i < len(keyValue) ; i++  {
		if _,ok := sdsMap[keyValue[i][0]];ok {
			return -2
		}
	}
	for i := 0; i < len(keyValue) ; i++   {
		sds.SetValue(keyValue[i][0],keyValue[i][1])
	}
	return 1
}

/**
 * Redis Incr 命令将 key 中储存的数字值增一。
 * 如果 key 不存在，那么 key 的值会先被初始化为 0 ，然后再执行 INCR 操作。
 * 如果值包含错误的类型，或字符串类型的值不能表示为数字，那么返回一个错误。
 */
func (sds *SdsString)Incr(key string) error  {
	if val,ok := sdsMap[key]; ok {
		if valNum,err := strconv.ParseInt(sdk.ByteString(val.value),10,64); err == nil {
			atomic.AddInt64(&valNum,1)
			val.value = []byte(strconv.FormatInt(valNum,10))
			sdsMap[key] = val
		} else {
			return errors.New("type cannot be converted")
		}
	} else {
		sds.SetnxValue(key,"1")
	}
	return nil
}


func (sds *SdsString)SaveString()  {
	str := "set"
	for k,v := range sdsMap  {
		str = str+" "+k+" "+sdk.ByteString(v.value)
	}
	sdk.WriteFile("save/appendonly.aof",str,os.O_WRONLY | os.O_CREATE | os.O_APPEND|os.O_TRUNC)
}

func GetMap() map[string]SdsString {
	return sdsMap
}

func SetMap(sdsMapT map[string]SdsString)  {
	sdsMap = sdsMapT
}

func (sds *SdsString)SaveStringRDB()  {
	str := sdk.JsonEncode(sdsMap)
	sdk.WriteFile("save/appendonly.rdb",str,os.O_WRONLY | os.O_CREATE | os.O_TRUNC)
}